/**
 * Created by Administrator on 2019/1/7.
 */
package com.game.fg.service;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.drafts.Draft_6455;
import org.java_websocket.handshake.ServerHandshake;

import java.net.URI;
import java.net.URISyntaxException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

public class AuthServer {


    private WebSocketClient ws;
    private static Logger logger = LogManager.getLogger(AuthServer.class.getName());

    public WebSocketClient getWs() {
        return ws;
    }

    void start(String url) {
        try {
            ws = new WebSocketClient(new URI(url), new Draft_6455()) {
                @Override
                public void onMessage(String message) {
                    logger.debug("认证服务器 onMessage(String message)");
                }

                @Override
                public void onMessage(ByteBuffer blob) {
                    byte identity = blob.get(0);//1个字节
                    byte encode = blob.get(1);//1个字节
                    short length = blob.getShort(2);//2个字节
                    byte version = blob.get(4);//1个字节
                    byte reserve = blob.get(5);//1个字节
                    short cmd = blob.getShort(6);//2个字节
                    if (cmd >= 0) {
                        byte[] data = new byte[length - 8];
                        System.arraycopy(blob.array(), 8, data, 0, length - 8);
                        //处理请求
                        msgParse(cmd, data);
                    }
                }

                @Override
                public void onOpen(ServerHandshake handshake) {
                    logger.debug("认证服务器连接成功");
                    getServerInfo();
                }

                @Override
                public void onClose(int code, String reason, boolean remote) {
                    logger.debug("认证服务器连接断开");
                }

                @Override
                public void onError(Exception ex) {
                    ex.printStackTrace();
                }
            };
            ws.connect();
        } catch (URISyntaxException ex) {
            ex.printStackTrace();
        }
    }

    //向认证服务器请求serverID和gameID
    private void getServerInfo() {
        String serverIP = FGConfig.getInstance().serverConfig.getWs_ip();//IP
        char[] bServerIP = new char[16];
        System.arraycopy(serverIP.toCharArray(), 0, bServerIP, 0, serverIP.toCharArray().length);
        ByteBuffer bb = ByteBuffer.allocate(20);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.put(new String(bServerIP).getBytes());//服务器IP
        bb.putShort((short) FGConfig.getInstance().serverConfig.getWsPort());//服务器监听端口
        bb.putShort((short) 0);//usLoadSrc
        FGWebSocket.sendCustom(ws, FGConst.S2AUTH_LoadServerInfo, bb.array());
        logger.debug(String.format("向认证服务器请求服务器信息 serverIp:%s severPort:%d", FGConfig.getInstance().serverConfig.getWs_ip(), FGConfig.getInstance().serverConfig.getWsPort()));
    }

    //玩家登陆验证
    void authUserLogin(int uid, String token) {
        char[] bToken = new char[64];
        System.arraycopy(token.toCharArray(), 0, bToken, 0, token.toCharArray().length);
        ByteBuffer bb = ByteBuffer.allocate(76);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(uid);
        bb.putShort(ManagerServer.serverInfo.gameID);
        bb.putShort(ManagerServer.serverInfo.serverID);
        bb.putShort(ManagerServer.serverInfo.serverLevel);
        bb.put(new String(bToken).getBytes());
        FGWebSocket.sendCustom(ws, FGConst.S2AUTH_UserLogin, bb.array());
        logger.debug(String.format("请求验证玩家token uid:%d token:%s", uid, token));
    }

    //回写用户数据
    void writeUserData(FGUser user) {

        long curTime = System.currentTimeMillis() / 1000;
        long playTime = curTime - user.getEnterGameTime();
        ByteBuffer bb = ByteBuffer.allocate(76);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(user.getUid());//玩家uid
        bb.putInt((int) (user.getTotalOnlineTime() + playTime));//总在线时长
        bb.putInt((int) (user.getTotalPlayTime() + playTime));//总游戏时长
        bb.putLong(user.getTotalWinLoseScore());//总净分
        bb.putLong(user.getTotalTax());//总税收
        bb.putInt((int) (user.getDayOnlineTime() + playTime));//当天在线时长
        bb.putInt((int) (user.getDayPlayTime() + playTime));//当天游戏时长
        bb.putInt(user.getDayWinLoseScore());//当天净分
        bb.putInt(user.getDayTax());//当天税收
        bb.putInt(user.getDayHelpCoin());//当天补助金
        bb.putInt((int) curTime);//数据库更新时间

        bb.putInt(user.getDayBuyTNum());//加积分次数
        bb.putInt(user.getDayBuyCNum());//加积分数
        bb.putInt(user.getDaySaleTNum());//扣积分次数
        bb.putInt(user.getDaySaleCNum());//扣积分数
        bb.putInt((int) curTime);//积分交换, 数据更新时间, unix时间戳

        bb.putShort(ManagerServer.serverInfo.gameID);//gameID
        bb.putShort(ManagerServer.serverInfo.serverID);//serverID
        FGWebSocket.sendCustom(ws, FGConst.S2AUTH_WriteUserData, bb.array());
        logger.debug(String.format("回写用户数据 uid:%d 总净分:%d 总税收:%d 当天净分:%d 当天税收:%d 当天补助金:%d 总在线时长:%d 总游戏时长:%d 当天在线时长:%d 当天游戏时长:%d 加积分次数:%d 加积分数:%d 扣积分次数:%d 扣积分数:%d",
                user.getUid(),
                user.getTotalWinLoseScore(),
                user.getTotalTax(),
                user.getDayWinLoseScore(),
                user.getDayTax(),
                user.getDayHelpCoin(),
                (int) (user.getTotalOnlineTime() + playTime),
                (int) (user.getTotalPlayTime() + playTime),
                (int) (user.getDayOnlineTime() + playTime),
                (int) (user.getDayPlayTime() + playTime),
                user.getDayBuyTNum(),
                user.getDayBuyCNum(),
                user.getDaySaleTNum(),
                user.getDaySaleCNum()));
    }

    //心跳包
    void KeepAlive() {
        if (ws.isOpen()) {
            FGWebSocket.sendCustom(ws, FGConst.CMD_KEEP_ALIVE, new byte[]{});
        }
    }

    //删除玩家在线状态
    void DeleteOnlineState(int uid) {
        ByteBuffer bb = ByteBuffer.allocate(8);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(uid);
        bb.putShort(ManagerServer.serverInfo.gameID);
        bb.putShort(ManagerServer.serverInfo.serverID);
        FGWebSocket.sendCustom(ws, FGConst.S2AUTH_DeleteOnlineState, bb.array());
        logger.debug("请求认证服务器删除玩家在线状态 uid:" + uid);
    }

    //回写用户金币
    void WriteUserMoney(int uid, int money, int optSrc) {
        ByteBuffer bb = ByteBuffer.allocate(20);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.putInt(uid);//玩家uid
        bb.putInt(money);//该值为增量
        bb.putInt(optSrc);//操作源
        bb.putShort(ManagerServer.serverInfo.gameID);//gameID
        bb.putShort(ManagerServer.serverInfo.serverID);//serverID
        bb.putInt(1);//true 返回消息
        FGWebSocket.sendCustom(ws, FGConst.S2AUTH_WriteUserMoney, bb.array());
        logger.debug(String.format("回写用户金币 uid:%d money:%d optSrc:%d", uid, money, optSrc));
    }

    private void msgParse(int cmd, byte[] data) {
        ByteBuffer bb = ByteBuffer.allocate(data.length);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bb.put(data);
        bb.rewind();
        switch (cmd) {
            case FGConst.AUTH2S_LoadServerInfo: {
                ManagerServer.serverInfo.gameID = bb.getShort(0);
                ManagerServer.serverInfo.serverID = bb.getShort(2);
                ManagerServer.serverInfo.serverLevel = bb.getShort(4);
                ManagerServer.serverInfo.serverPort = bb.getShort(6);
                ManagerServer.serverInfo.maxScore = bb.getInt(8);
                ManagerServer.serverInfo.minScore = bb.getInt(12);
                ManagerServer.serverInfo.maxPlayer = bb.getInt(16);
                ManagerServer.serverInfo.firstTax = bb.getInt(20);
                ManagerServer.serverInfo.secondTax = bb.getInt(24);
                byte[] bServerName = new byte[32];
                byte[] bServerIp = new byte[16];
                bb.position(28);
                bb.get(bServerName);
                bb.position(60);
                bb.get(bServerIp);
                ManagerServer.serverInfo.serverName = new String(bServerName).trim();
                ManagerServer.serverInfo.serverIP = new String(bServerIp).trim();
                ManagerServer.serverInfo.serverStatus = bb.get(76);
                logger.debug(String.format("认证服务器返回服务器数据 gameID:%d serverID:%d serverIP:%s serverPort:%d serverStatus:%d maxPlayer:%d",
                        ManagerServer.serverInfo.gameID,
                        ManagerServer.serverInfo.serverID,
                        ManagerServer.serverInfo.serverIP,
                        ManagerServer.serverInfo.serverPort,
                        ManagerServer.serverInfo.serverStatus,
                        ManagerServer.serverInfo.maxPlayer));
                //连接管理服务器注册服务器信息
                if (ManagerServer.getInstance().getWs() == null || ManagerServer.getInstance().getWs().isClosed()) {
                    ManagerServer.getInstance().start(FGConfig.getInstance().getManagerServerUrl());
                }
            }
            break;
            case FGConst.AUTH2S_UserLogin: {
                //验证玩家结果返回
                int uid = bb.getInt(0);
                int tokenValidTime = bb.getInt(4);
                short retNum = bb.getShort(8);
                FGUser user = FGUserManager.getInstance().getUser(uid);
                if (user != null) {
                    if (retNum < 0) {
                        FGWebSocket.send(user.getWs(), FGConst.S2C_EnterGame, MessageBuild.responseEnterGame(Message.EnumErrorCode.INVALIDTOKEN, 0, 0, 0, 0, 0, 0, 0, 0, 0));
                        FGUserManager.getInstance().removeUser(user.getWs(), "token验证失败");
                        logger.warn(String.format("认证服务器验证玩家失败 uid:%d retNum:%d", uid, retNum));
                    } else {
                        logger.debug(String.format("认证服务器验证玩家成功 uid:%d retNum:%d", uid, retNum));
                    }
                }
            }
            break;
            case FGConst.AUTH2S_UserInfo: {
                //验证成功发送玩家数据
                int uid = bb.getInt();
                int iGameCoin = bb.getInt();                  //score
                int iTotalOnlineTime = bb.getInt();           //总在线时长
                int iTotalPlayTime = bb.getInt();             //总游戏时长
                long llTotalWinLoseScore = bb.getLong();      //总净分: 输赢分
                long llTotalTax = bb.getLong();               //总税收
                int iDayOnlineTime = bb.getInt();             //当天在线时长
                int iDayPlayTime = bb.getInt();               //当天游戏时长
                int iDayWinLoseScore = bb.getInt();           //当天净分: 输赢分
                int iDayTax = bb.getInt();                    //当天税收
                int iDayHelpCoin = bb.getInt();               //当天补助金
                int iDayUpdateTime = bb.getInt();             //净分/税收等, 数据更新时间, unix时间戳
                byte[] appID = new byte[256];                 //appID
                bb.get(appID);

                int iCfgBuyTNum = bb.getInt();                //配置: 加积分次数
                int iCfgBuyCNum = bb.getInt();                //配置: 加积分数
                int iCfgSaleTNum = bb.getInt();               //配置: 扣积分次数
                int iCfgSaleCNum = bb.getInt();               //配置: 扣积分数
                int iDBuyTNum = bb.getInt();                  //加积分次数
                int iDBuyCNum = bb.getInt();                  //加积分数
                int iDSaleTNum = bb.getInt();                 //扣积分次数
                int iDSaleCNum = bb.getInt();                 //扣积分数
                int iDUpdateTime = bb.getInt();               //积分交换, 数据更新时间, unix时间戳

                FGUser user = FGUserManager.getInstance().getUser(uid);
                if (user != null) {
                    //如果跨天了则重置玩家每日相关数据
                    int userZeroTime = iDayUpdateTime / (1000 * 3600 * 24) * (1000 * 3600 * 24) / 1000;
                    int zeroTime = (int) System.currentTimeMillis() / (1000 * 3600 * 24) * (1000 * 3600 * 24) / 1000;
                    if (userZeroTime != zeroTime) {
                        iDayWinLoseScore = 0;
                        iDayTax = 0;
                        iDayHelpCoin = 0;
                        logger.debug(String.format("玩家跨天登陆 重置玩家每日相关数据 uid:%d iDayUpdateTime:%d", uid, iDayUpdateTime));
                    }
                    //设置玩家数据
                    user.setUserInfo(iGameCoin, llTotalWinLoseScore, llTotalTax, iDayWinLoseScore, iDayTax,
                            iDayHelpCoin, new String(appID).trim(), iTotalOnlineTime, iTotalPlayTime, iDayOnlineTime,
                            iDayPlayTime, iCfgBuyTNum, iCfgBuyCNum, iCfgSaleTNum, iCfgSaleCNum,
                            iDBuyTNum, iDBuyCNum, iDSaleTNum, iDSaleCNum);

                    FGWebSocket.send(user.getWs(), FGConst.S2C_EnterGame, MessageBuild.responseEnterGame(Message.EnumErrorCode.SUCCESS, iGameCoin,
                            iCfgBuyTNum, iCfgBuyCNum, iCfgSaleTNum, iCfgSaleCNum, iDBuyTNum, iDBuyCNum, iDSaleTNum, iDSaleCNum));

                    //MQ日志
                    MQServer.getInstance().onUserLogin(user);
                    logger.debug(String.format("玩家成功进入游戏大厅 uid:%d money:%d 总净分:%d 总税收:%d 当天净分:%d 当天税收:%d 当天补助金:%d 总在线时长:%d 总游戏时长:%d 当天在线时长:%d 当天游戏时长:%d appID:%s",
                            user.getUid(),
                            iGameCoin,
                            user.getTotalWinLoseScore(),
                            user.getTotalTax(),
                            user.getDayWinLoseScore(),
                            user.getDayTax(),
                            user.getDayHelpCoin(),
                            user.getTotalOnlineTime(),
                            user.getTotalPlayTime(),
                            user.getDayOnlineTime(),
                            user.getDayPlayTime(),
                            new String(appID).trim()));
                }
            }
            break;
            case FGConst.AUTH2S_WriteUserMoney: {
                int uid = bb.getInt();
                int leftMoney = bb.getInt();//返回身上积分
                int retNum = bb.getInt();//0: 操作成功
                int optSrc = bb.getInt();//操作源, 原值返回
                FGUser user = FGUserManager.getInstance().getUser(uid);
                if (user != null) {
                    //TODO:做你想做的
                    if (retNum == 0) {
                        if (user.getMoney() != leftMoney) {
                            logger.warn(String.format("回写金币返回 金币数量异常 uid:%d leftMoney:%d retMoney:%d optSrc:%d", uid, user.getMoney(), leftMoney, optSrc));
                        }
                        logger.debug(String.format("回写金币返回成功 uid:%d leftMoney:%d retMoney:%d optSrc:%d", uid, user.getMoney(), leftMoney, optSrc));
                    } else {
                        logger.warn(String.format("回写金币返回错误 uid:%d leftMoney:%d retMoney:%d optSrc:%d retNum:%d", uid, user.getMoney(), leftMoney, optSrc, retNum));
                    }
                }
            }
            break;
        }
    }


    private AuthServer() {
    }

    private static AuthServer as = new AuthServer();

    public static AuthServer getInstance() {
        return as;
    }
}
